/**
 * @file rtk_ipc.c
 * @author LokLiang (lokliang@163.com)
 * @brief
 * @version 0.1
 * @date 2023-05-01
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "prv_rtk_class.h"

#include <stdarg.h>

static void *_rtk_ipc_fifo_alloc(void *fifo_handle, va_list arp);
static void *_rtk_ipc_fifo_take(void *fifo_handle, va_list arp);
static void *_rtk_ipc_queue_recv(void *queue_handle, va_list arp);
static void *_rtk_ipc_queue_send(void *queue_handle, va_list arp);
static void *_rtk_ipc_queue_alloc(void *queue_handle, va_list arp);
static void *_rtk_ipc_queue_take(void *queue_handle, va_list arp);

struct rtk_fifo_q_handle
{
    k_fifo_t handle;
    struct rtk_sem_handle sem_take;
};

rtk_err_t rtk_fifo_q_create(rtk_fifo_t *fifo_handle, const char *name, rtk_sem_type type)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_ASS_FALSE(fifo_handle == NULL, "");

    rtk_err_t ret;

    do
    {
        struct rtk_fifo_q_handle *fifo = _rtk_name_malloc(name, sizeof(*fifo));
        if (fifo == NULL)
        {
            fifo_handle->hdl = NULL;
            ret = RTK_NOMEM;
            break;
        }

        memset(fifo, 0, sizeof(*fifo));

        if (k_fifo_q_create(&fifo->handle) != 0)
        {
            rtk_heapk_free(fifo);
            ret = RTK_NOMEM;
            break;
        }

        _rtk_sem_init(&fifo->sem_take, 0, 1, type);
        fifo_handle->hdl = fifo;

        ret = RTK_OK;

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_fifo_q_delete(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(fifo_handle);

    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    rtk_err_t ret;
    size_t sched_nest;

    sched_nest = rtk_sched_suspend();
    if ((ret = rtk_heapk_free(fifo)) == RTK_OK)
    {
        k_fifo_q_delete(&fifo->handle);
        fifo_handle->hdl = NULL;
    }
    rtk_sched_resume(sched_nest);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_fifo_q_clr(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(fifo_handle);

    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    k_fifo_q_clr(&fifo->handle);

    _RTK_TRACE_EXIT();
    return RTK_OK;
}

static void *_rtk_ipc_fifo_alloc(void *fifo_handle, va_list arp)
{
    size_t size = va_arg(arp, size_t);
    return k_fifo_alloc(size);
}

void rtk_fifo_q_regist(rtk_fifo_t *fifo_handle, rtk_work_t *work_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    k_fifo_q_regist(&fifo->handle, (k_work_t *)work_handle, delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_fifo_q_unregist(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    k_fifo_q_unregist(&fifo->handle);
    _RTK_TRACE_EXIT();
}

void *rtk_fifo_alloc(size_t size, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_SWAP(wait_ticks);
    void *ret = _rtk_wait_memory(_rtk_ipc_fifo_alloc, NULL, &cm_rtk->heap_sem, wait_ticks, size);
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_fifo_free(void *fifo_data)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();

    rtk_err_t ret;
    if (k_fifo_free(fifo_data) == 0)
    {
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_fifo_put(rtk_fifo_t *fifo_handle, void *fifo_data)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE_ERR(fifo_handle);

    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    rtk_err_t ret;

    if (k_fifo_put(&fifo->handle, fifo_data) == 0)
    {
        _rtk_sem_release(&fifo->sem_take);
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

static void *_rtk_ipc_fifo_take(void *fifo_handle, va_list arp)
{
    _RTK_UNUSE(arp);
    return k_fifo_take(fifo_handle);
}

void *rtk_fifo_take(rtk_fifo_t *fifo_handle, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(fifo_handle);
    _RTK_CHECK_SWAP(wait_ticks);

    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    void *ret = _rtk_wait_memory(_rtk_ipc_fifo_take, &fifo->handle, &fifo->sem_take, wait_ticks);

    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_fifo_peek_head(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(fifo_handle);
    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    void *ret = k_fifo_peek_head(&fifo->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_fifo_peek_tail(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(fifo_handle);
    struct rtk_fifo_q_handle *fifo = fifo_handle->hdl;
    void *ret = k_fifo_peek_tail(&fifo->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_fifo_q_is_valid(rtk_fifo_t *fifo_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = _rtk_service_handle_is_valid(fifo_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

struct rtk_queue_handle
{
    k_queue_t handle;
    struct rtk_sem_handle sem_send;
    struct rtk_sem_handle sem_recv;
};

rtk_err_t rtk_queue_create(rtk_queue_t *queue_handle,
                           const char *name,
                           rtk_sem_type type,
                           size_t queue_length,
                           size_t item_size)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_ASS_FALSE(queue_handle == NULL, "");
    _RTK_ASS_FALSE(queue_length == 0, "");
    _RTK_ASS_FALSE(item_size == 0, "");

    rtk_err_t ret;

    do
    {
        struct rtk_queue_handle *queue = _rtk_name_malloc(name, sizeof(*queue));
        if (queue == NULL)
        {
            queue_handle->hdl = NULL;
            ret = RTK_NOMEM;
            break;
        }

        memset(queue, 0, sizeof(*queue));

        if (k_queue_create(&queue->handle, queue_length, item_size) != 0)
        {
            rtk_heapk_free(queue);
            ret = RTK_NOMEM;
            break;
        }

        _rtk_sem_init(&queue->sem_recv, 0, 1, type);
        _rtk_sem_init(&queue->sem_send, 0, 1, type);
        queue_handle->hdl = queue;

        ret = RTK_OK;

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_queue_delete(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(queue_handle);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    rtk_err_t ret;
    size_t sched_nest;

    sched_nest = rtk_sched_suspend();
    if ((ret = rtk_heapk_free(queue)) == RTK_OK)
    {
        k_queue_delete(&queue->handle);
        queue_handle->hdl = NULL;
    }
    rtk_sched_resume(sched_nest);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_queue_clr(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE_ERR(queue_handle);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    k_queue_clr(&queue->handle);

    _RTK_TRACE_EXIT();
    return RTK_OK;
}

void rtk_queue_regist(rtk_queue_t *queue_handle, rtk_work_t *work_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    struct rtk_queue_handle *queue = queue_handle->hdl;
    k_queue_regist(&queue->handle, (k_work_t *)work_handle, delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_queue_unregist(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_queue_handle *queue = queue_handle->hdl;
    k_queue_unregist(&queue->handle);
    _RTK_TRACE_EXIT();
}

static void *_rtk_ipc_queue_recv(void *queue_handle, va_list arp)
{
    void *dst = va_arg(arp, void *);
    if (k_queue_recv(queue_handle, dst) == 0)
    {
        return (void *)!NULL;
    }
    else
    {
        return NULL;
    }
}

rtk_err_t rtk_queue_recv(rtk_queue_t *queue_handle, void *dst, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE_ERR(queue_handle);
    _RTK_CHECK_SWAP(wait_ticks);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    rtk_err_t ret;

    if (_rtk_wait_memory(_rtk_ipc_queue_recv, &queue->handle, &queue->sem_recv, wait_ticks, dst) == NULL)
    {
        ret = RTK_TIMEOUT;
    }
    else
    {
        _rtk_sem_release(&queue->sem_send);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

static void *_rtk_ipc_queue_send(void *queue_handle, va_list arp)
{
    const void *src = va_arg(arp, void *);
    if (k_queue_send(queue_handle, src) == 0)
    {
        return (void *)!NULL;
    }
    else
    {
        return NULL;
    }
}

rtk_err_t rtk_queue_send(rtk_queue_t *queue_handle, const void *src, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE_ERR(queue_handle);
    _RTK_CHECK_SWAP(wait_ticks);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    rtk_err_t ret;

    if (_rtk_wait_memory(_rtk_ipc_queue_send, &queue->handle, &queue->sem_send, wait_ticks, src) == NULL)
    {
        ret = RTK_TIMEOUT;
    }
    else
    {
        _rtk_sem_release(&queue->sem_recv);
        ret = RTK_OK;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

static void *_rtk_ipc_queue_alloc(void *queue_handle, va_list arp)
{
    _RTK_UNUSE(arp);
    return k_queue_alloc(queue_handle);
}

void *rtk_queue_alloc(rtk_queue_t *queue_handle, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(queue_handle);
    _RTK_CHECK_SWAP(wait_ticks);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    void *ret = _rtk_wait_memory(_rtk_ipc_queue_alloc, &queue->handle, &queue->sem_send, wait_ticks);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_queue_free(void *queue_data)
{
    _RTK_TRACE_ENTRY();
    rtk_err_t ret;

    if (k_queue_free(queue_data) == 0)
    {
        k_queue_t *handle = k_queue_read_handle(queue_data);
        struct rtk_queue_handle *queue = _RTK_CONTAINER_OF(handle, struct rtk_queue_handle, handle);
        _rtk_sem_release(&queue->sem_send);
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_queue_put(void *queue_data)
{
    _RTK_TRACE_ENTRY();
    rtk_err_t ret;

    if (k_queue_put(queue_data) == 0)
    {
        k_queue_t *handle = k_queue_read_handle(queue_data);
        struct rtk_queue_handle *queue = _RTK_CONTAINER_OF(handle, struct rtk_queue_handle, handle);
        _rtk_sem_release(&queue->sem_recv);
        ret = RTK_OK;
    }
    else
    {
        ret = RTK_E_PARAM;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

static void *_rtk_ipc_queue_take(void *queue_handle, va_list arp)
{
    _RTK_UNUSE(arp);
    return k_queue_take(queue_handle);
}

void *rtk_queue_take(rtk_queue_t *queue_handle, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(queue_handle);
    _RTK_CHECK_SWAP(wait_ticks);

    struct rtk_queue_handle *queue = queue_handle->hdl;
    void *ret = _rtk_wait_memory(_rtk_ipc_queue_take, &queue->handle, &queue->sem_recv, wait_ticks);

    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_queue_peek_head(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(queue_handle);
    struct rtk_queue_handle *queue = queue_handle->hdl;
    void *ret = k_queue_peek_head(&queue->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

void *rtk_queue_peek_tail(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE(queue_handle);
    struct rtk_queue_handle *queue = queue_handle->hdl;
    void *ret = k_queue_peek_tail(&queue->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_queue_get_item_size(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_queue_handle *queue = queue_handle->hdl;
    size_t ret = k_queue_get_item_size(&queue->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_queue_is_valid(rtk_queue_t *queue_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = _rtk_service_handle_is_valid(queue_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

struct rtk_pipe_handle
{
    k_pipe_t handle;
};

rtk_err_t rtk_pipe_create(rtk_pipe_t *pipe_handle, const char *name, size_t pipe_size, rtk_sem_type type)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_ASS_FALSE(pipe_handle == NULL, "");
    rtk_err_t ret;

    do
    {
        struct rtk_pipe_handle *pipe = _rtk_name_malloc(name, sizeof(*pipe));
        if (pipe == NULL)
        {
            pipe_handle->hdl = NULL;
            ret = RTK_NOMEM;
        }

        memset(pipe, 0, sizeof(*pipe));

        if (k_pipe_create(&pipe->handle, pipe_size) != 0)
        {
            rtk_heapk_free(pipe);
            ret = RTK_NOMEM;
        }

        pipe_handle->hdl = pipe;

        ret = RTK_OK;

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_pipe_delete(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(pipe_handle);

    struct rtk_pipe_handle *pipe = pipe_handle->hdl;

    k_pipe_delete(&pipe->handle);
    rtk_heapk_free(pipe);
    pipe_handle->hdl = NULL;

    _RTK_TRACE_EXIT();
    return RTK_OK;
}

rtk_err_t rtk_pipe_clr(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_HANDLE_ERR(pipe_handle);

    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    k_pipe_clr(&pipe->handle);

    _RTK_TRACE_EXIT();
    return RTK_OK;
}

void rtk_pipe_regist(rtk_pipe_t *pipe_handle, rtk_work_t *work_handle, rtk_tick_t delay_ticks)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    k_pipe_regist(&pipe->handle, (k_work_t *)work_handle, delay_ticks);
    _RTK_TRACE_EXIT();
}

void rtk_pipe_unregist(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    k_pipe_unregist(&pipe->handle);
    _RTK_TRACE_EXIT();
}

size_t rtk_pipe_poll_write(rtk_pipe_t *pipe_handle, uint8_t data)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_poll_write(&pipe->handle, data);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_pipe_fifo_fill(rtk_pipe_t *pipe_handle, const void *data, size_t size)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_fifo_fill(&pipe->handle, data, size);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_pipe_poll_read(rtk_pipe_t *pipe_handle, uint8_t *data)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_poll_read(&pipe->handle, data);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_pipe_fifo_read(rtk_pipe_t *pipe_handle, void *data, size_t size)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_fifo_read(&pipe->handle, data, size);
    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_pipe_is_ne(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    bool ret = k_pipe_is_ne(&pipe->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_pipe_get_valid_size(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_get_valid_size(&pipe->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

size_t rtk_pipe_get_empty_size(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    size_t ret = k_pipe_get_empty_size(&pipe->handle);
    _RTK_TRACE_EXIT();
    return ret;
}

void rtk_pipe_peek_valid(rtk_pipe_t *pipe_handle, void **dst_base, size_t *dst_sizee)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    k_pipe_peek_valid(&pipe->handle, dst_base, dst_sizee);
    _RTK_TRACE_EXIT();
}

void rtk_pipe_peek_empty(rtk_pipe_t *pipe_handle, void **dst_base, size_t *dst_sizee)
{
    _RTK_TRACE_ENTRY();
    struct rtk_pipe_handle *pipe = pipe_handle->hdl;
    k_pipe_peek_empty(&pipe->handle, dst_base, dst_sizee);
    _RTK_TRACE_EXIT();
}

bool rtk_pipe_is_valid(rtk_pipe_t *pipe_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = _rtk_service_handle_is_valid(pipe_handle);
    _RTK_TRACE_EXIT();
    return ret;
}
